package engine.loader.texture;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Hashtable;

import javax.imageio.ImageIO;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;

import static org.lwjgl.opengl.GL11.*;

public class TextureLoader_Deprecated {
	private static HashMap<String, Texture_Deprecated> mapStringTextures = new HashMap<String, Texture_Deprecated>();
	private static HashMap<String, BufferedImage> mapStringImage = new HashMap<String, BufferedImage>();
	
	private static ColorModel glAlphaColorModel = new ComponentColorModel(
			ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[]{8, 8, 8, 8},
			true, false, ComponentColorModel.TRANSLUCENT, DataBuffer.TYPE_BYTE);

	private static ColorModel glColorModel = new ComponentColorModel(ColorSpace
			.getInstance(ColorSpace.CS_sRGB), new int[]{8, 8, 8, 0}, false,
			false, ComponentColorModel.OPAQUE, DataBuffer.TYPE_BYTE);

	private TextureLoader_Deprecated(){};
	
	public static Texture_Deprecated getTexture(String source, int minFilter, int magFilter, boolean mipmapped) {
		Texture_Deprecated tex = mapStringTextures.get(source);
		
		if(tex != null) {
			if(tex.getMinFilter() == minFilter && tex.getMagFilter() == magFilter && tex.isMipMapped() == mipmapped) {
				return tex;
			}
		}
		
		tex = getTexture(loadImage(source), minFilter, magFilter, mipmapped);
		
		if(tex != null) {
			mapStringTextures.put(source, tex);
		} else {
			System.err.println("Occured while loading " + source);
		}
		
		return tex;
	}
	
	public static Texture_Deprecated getTexture(BufferedImage source, int minFilter, int magFilter, boolean mipmapped) {
		int maxsize = glGetInteger(GL_MAX_TEXTURE_SIZE);
		if(source.getWidth() > maxsize || source.getHeight() > maxsize) {
			System.err.println("Image is too large for your System");
			return null;
		}
		
		if(!isPowerOfTwo(source.getWidth()) || !isPowerOfTwo(source.getHeight())) {
			System.err.println("Image has a size, which is not a power of two");
			return null;
		}
		
		int target = GL_TEXTURE_2D;
		int srcPixelFormat = 0;
		int dstPixelFormat = GL_RGBA;
		int textureID = glGenTextures();
		
		Texture_Deprecated texture = new Texture_Deprecated(target, textureID, minFilter, magFilter, mipmapped);
		
		glBindTexture(target, textureID);
		
		if (source.getColorModel().hasAlpha()) {
			srcPixelFormat = GL_RGBA;
		} else {
			srcPixelFormat = GL_RGB;
		}
		
		ByteBuffer textureBuffer = convertImageData(source);
		
		GL11.glTexParameteri(target, GL11.GL_TEXTURE_MIN_FILTER, minFilter);
		GL11.glTexParameteri(target, GL11.GL_TEXTURE_MAG_FILTER, magFilter);
		
		if (mipmapped) {
			GLU.gluBuild2DMipmaps(target, dstPixelFormat,
					source.getWidth(), source.getHeight(), srcPixelFormat,
					GL11.GL_UNSIGNED_BYTE, textureBuffer);
		} else {
			GL11.glTexImage2D(target, 0, dstPixelFormat, source.getWidth(), 
					source.getHeight(), 0,
					srcPixelFormat, GL11.GL_UNSIGNED_BYTE, textureBuffer);
		}
		
		return texture;
	}

	private static ByteBuffer convertImageData(BufferedImage source) {
		int texWidth = source.getWidth();
		int texHeight = source.getHeight();
		
		WritableRaster raster;
		BufferedImage texImage;
		
		if (source.getColorModel().hasAlpha()) {
			raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
					texWidth, texHeight, 4, null);
			texImage = new BufferedImage(glAlphaColorModel, raster, false,
					new Hashtable<String, Object>());
		} else {
			raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
					texWidth, texHeight, 3, null);
			texImage = new BufferedImage(glColorModel, raster, false,
					new Hashtable<String, Object>());
		}
		
		Graphics2D g = texImage.createGraphics();
		g.setColor(new Color(0f, 0f, 0f, 0f));
		g.fillRect(0, 0, texWidth, texHeight);
		g.drawImage(source, 0, 0, texWidth, texHeight, 0, texHeight, texWidth, 0, null);
		
		byte[] data = ((DataBufferByte)texImage.getRaster().getDataBuffer()).getData();
		
		ByteBuffer imageBuffer = BufferUtils.createByteBuffer(data.length);
		imageBuffer.order(ByteOrder.nativeOrder());
		imageBuffer.put(data);
		imageBuffer.flip();
		
		return imageBuffer;
	}
	
	public static BufferedImage loadImage(String source) {
		BufferedImage buf = mapStringImage.get(source);
		
		if(buf == null) {
			URL url = TextureLoader_Deprecated.class.getClassLoader().getResource(source);
			File f = new File(source);
			
			if(url == null && !f.exists()) {
				System.err.println("Cannot find " + source);
				return null;
			}
			
			try {
				if(url != null) {
					buf = ImageIO.read(new BufferedInputStream(TextureLoader_Deprecated.class.getClassLoader().getResourceAsStream(source)));
				} else {
					buf = ImageIO.read(f);
				}
			} catch (IOException e) {
				System.err.println("Error while loading " + source);
				return null;
			}
			
			mapStringImage.put(source, buf);
		}
		
		return buf;
	}
	
	private static boolean isPowerOfTwo(int size) {
		int i = 1;
		
		while(i <= size) {
			if(size == i) {
				return true;
			}
			i *= 2;
		}
		
		return true;
	}
}
